package net.sf.fmj.media.codec.audio.ulaw;

/**
 * Turns 8-bit mu-law bytes back into 16-bit PCM values. Adapted from code by
 * Marc Sweetgall at http://www.codeproject.com/csharp/g711audio.asp Note: in
 * .Net, a byte is unsigned, hence the need to always do byte & 0xff - this
 * converts an unsigned byte to the corresponding (nonnegative) int value.
 */
public class MuLawDecoderUtil {
	/**
	 * An array where the index is the mu-law input, and the value is the 16-bit
	 * PCM result.
	 * 
	 */
	private static short[] muLawToPcmMap;

	static {
		muLawToPcmMap = new short[256];
		for (short i = 0; i < muLawToPcmMap.length; i++)
			muLawToPcmMap[i] = decode((byte) i);
	}

	/**
	 * Decode one mu-law byte. For internal use only.
	 * 
	 * @param mulaw
	 *            The encoded mu-law byte
	 * @return A short containing the 16-bit result
	 */
	private static short decode(byte mulaw) {
		// Flip all the bits
		mulaw = (byte) ~mulaw;

		// Pull out the value of the sign bit
		int sign = mulaw & 0x80;
		// Pull out and shift over the value of the exponent
		int exponent = (mulaw & 0x70) >> 4;
		// Pull out the four bits of data
		int data = mulaw & 0x0f;

		// Add on the implicit fifth bit (we know the four data bits followed a
		// one bit)
		data |= 0x10;
		/*
		 * Add a 1 to the end of the data by shifting over and adding one. Why?
		 * Mu-law is not a one-to-one function. There is a range of values that
		 * all map to the same mu-law byte. Adding a one to the end essentially
		 * adds a "half byte", which means that the decoding will return the
		 * value in the middle of that range. Otherwise, the mu-law decoding
		 * would always be less than the original data.
		 */
		data <<= 1;
		data += 1;
		/*
		 * Shift the five bits to where they need to be: left (exponent + 2)
		 * places Why (exponent + 2) ? 1 2 3 4 5 6 7 8 9 A B C D E F G . 7 6 5 4
		 * 3 2 1 0 . . . . . . . <-- starting bit (based on exponent) . . . . .
		 * . . . . . 1 x x x x 1 <-- our data We need to move the one under the
		 * value of the exponent, which means it must move (exponent + 2) times
		 */
		data <<= exponent + 2;
		// Remember, we added to the original, so we need to subtract from the
		// final
		data -= MuLawEncoderUtil.BIAS;
		// If the sign bit is 0, the number is positive. Otherwise, negative.
		return (short) (sign == 0 ? data : -data);
	}

	/**
	 * Decode one mu-law byte
	 * 
	 * @param mulaw
	 *            The encoded mu-law byte
	 * @return A short containing the 16-bit result
	 */
	public static short muLawDecode(byte mulaw) {
		return muLawToPcmMap[mulaw & 0xff];
	}

	// /**
	// * Decode an array of mu-law encoded bytes
	// *
	// * @param data An array of mu-law encoded bytes
	// * @return An array of shorts containing the results
	// */
	// public static void muLawDecode(byte[] data, short[] decoded)
	// {
	// int size = data.length;
	// //short[] decoded = new short[size];
	// for (int i = 0; i < size; i++)
	// decoded[i] = muLawToPcmMap[data[i]];
	// //return decoded;
	// }

	/**
	 * Decode an array of mu-law encoded bytes
	 * 
	 * @param data
	 *            An array of mu-law encoded bytes
	 * @param decoded
	 *            An array of bytes in Little-Endian format containing the
	 *            results, must be twice as big as data.
	 */
	public static void muLawDecodeLittleEndian(byte[] data, int offset,
			int len, byte[] decoded) {
		int size = len;
		// byte[] decoded = new byte[size * 2];
		for (int i = 0; i < size; i++) {
			// First byte is the less significant byte
			decoded[2 * i] = (byte) (muLawToPcmMap[data[offset + i] & 0xff] & 0xff);
			// Second byte is the more significant byte
			decoded[2 * i + 1] = (byte) (muLawToPcmMap[data[offset + i] & 0xff] >> 8);
		}
		// return decoded;
	}

	public static void muLawDecodeBigEndian(byte[] data, int offset, int len,
			byte[] decoded) {
		int size = len;
		// byte[] decoded = new byte[size * 2];
		for (int i = 0; i < size; i++) {
			// First byte is the more significant byte
			decoded[2 * i + 1] = (byte) (muLawToPcmMap[data[offset + i] & 0xff] & 0xff);
			// Second byte is the less significant byte
			decoded[2 * i] = (byte) (muLawToPcmMap[data[offset + i] & 0xff] >> 8);
		}
		// return decoded;
	}

	public static void muLawDecode(boolean bigEndian, byte[] data, int offset,
			int len, byte[] decoded) {
		if (bigEndian)
			muLawDecodeBigEndian(data, offset, len, decoded);
		else
			muLawDecodeLittleEndian(data, offset, len, decoded);
	}
}
